﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using ClassLibrary;

namespace ClientSite
{
    public partial class clientsite : Form
    {
        public clientsite()
        {
            CheckForIllegalCrossThreadCalls = false;
            InitializeComponent();
        }

        class Params
        {
            public int sr;
            public int er;
            public int id;
            public Params(int s, int e, int i)
            {
                sr = s;
                er = e;
                id = i;
            }
        };

        List<System.TimeSpan> timeListCalculateClient = new List<System.TimeSpan>();
        System.TimeSpan difffff;
        Socket client;
        IPEndPoint ipe;
        Thread connect;
        Boolean datareceiving = false;
        DateTime t1, timereceive;
        System.TimeSpan diffTimeSend;
        int finish = 0;//refes=1;
        double[,] A;
        double[,] B;
        double[,] C;
        int N;
        int[] stateLst = new int[20];
        class ServerState
        {
            public byte[] Buffer = new byte[1024 * 1024*10]; //buffer for network i/o
            public int DataSize = 0; //data size to be received by the server
            public bool DataSizeReceived = false; //whether prefix was received
            public MemoryStream Data = new MemoryStream(); //place where data is stored
            public Socket Client;   //client socket
        }

        public class ClientState
        {
            public Socket Client; //client socket
            public byte[] DataToSend; //data to be trasferred
            public int DataSent = 0; //data already sent
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            connect = new Thread(new ThreadStart(ConnectServer));
            connect.IsBackground = true;
            connect.Start();
            this.btnConnect.Enabled = false;
          //  txtResult.Text = "Connect successful";
        }

        public void ConnectServer()
        {
            ipe =new IPEndPoint(IPAddress.Parse(txtServerIp.Text),2013);
            client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
            client.Connect(ipe);
            txtResult.Text = "Connect successful";
            Init();
            
            Thread listening = new Thread(ListeningData);
            listening.IsBackground = true;
            listening.Start(client);
        }

        public void ListeningData(object obj)
        {
            Socket clientSK = (Socket)obj;
            ServerState state = new ServerState();//
            state.Client = clientSK;
            while (true)
            {
               
                while (datareceiving)
                {
                    
                }

                datareceiving = true;
                state.DataSize = 0;
                state.DataSizeReceived = false;
           //     state.Data = new MemoryStream();
               // int byteavailible = clientSK.Available;
                while (clientSK.Available == 0)
                {
 
                }
                 timereceive = DateTime.Now;
                clientSK.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, new AsyncCallback(ServerReadCallback), state);

            

               // byte[] buff = new byte[BUFERSIZE];
              //  int recieve = clientSK.Receive(buff);
              //  MaHoa(buff);
            }
        }
        /// <summary>
        /// Deserialize data and calcualator
        /// </summary>
        /// <param name="buff"></param>
        public myStruct MaHoa(byte[] buff)
        {
            MemoryStream stream = new MemoryStream(buff);
            BinaryFormatter bformat = new BinaryFormatter();
            myStruct data = (myStruct)bformat.Deserialize(stream);
            return data;
        }

        public void Calculate(myStruct data)
        {
            N = data.Matrixsize;
            C = new double[N, N];
            A = B = data.Data;
            int cnt = data.Thread;
            int str = data.Sr;
            int enr = data.Er;
            int sd = enr - str + 1;
            if (cnt == 1)
            {   //synchronous
                TinhTich(new Params(str, enr, 0));
            }
            else //asynchoronous
            {
                int i;
                Thread t;
                for (i = 0; i < cnt - 1; i++)
                {
                    stateLst[i] = 0;
                    t = new Thread(new ParameterizedThreadStart(TinhTich));
                    t.Start(new Params(str + i * sd / cnt, str + (i + 1) * sd / cnt, i));
                }
                TinhTich(new Params(str + (cnt - 1) * sd / cnt, enr, cnt - 1));
                for (i = 0; i < cnt - 1; i++)
                    while (stateLst[i] == 0) ; //wait
            }
            txtResult.ScrollToCaret();
            //send back to server
            DateTime timeExample = DateTime.Now;
            System.TimeSpan timeSpan = timeExample.Subtract(timeExample);
            myStruct mystr = new myStruct(client, C, 1, N, cnt, str, enr, 0, false, timeSpan, timeSpan,null);
            MemoryStream stream1 = new MemoryStream();
            BinaryFormatter bformat1 = new BinaryFormatter();
            bformat1.Serialize(stream1, mystr);

            // txtResult.AppendText("bien count la " + mystr.Count);
          //  byte[] buff1 = new byte[1024 * 1024 * 250];
         //   buff1 = stream1.ToArray();//convert to bytes
         //   client.Send(buff1);
           // client.Close();
            ClientState state1 = new ClientState();
            state1.Client = client;

            //add prefix to data
            Byte[] data1 = stream1.ToArray();
            state1.DataToSend = new byte[data1.Length + 4];
            byte[] prefix = BitConverter.GetBytes(data1.Length);
            //copy data size prefix
            Buffer.BlockCopy(prefix, 0, state1.DataToSend, 0, prefix.Length);
            //copy the data
            Buffer.BlockCopy(data1, 0, state1.DataToSend, prefix.Length, data1.Length);
            t1 = DateTime.Now;
           client.BeginSend(state1.DataToSend, 0, state1.DataToSend.Length,
              SocketFlags.None, new AsyncCallback(ClientSendCallback), state1);
        }

        /// <summary>
        /// set up data begining for server
        /// </summary>
        public void Init()
        {
            int t = Convert.ToInt32(txtNumberThread.Text);
            DateTime timeExample = DateTime.Now;
            System.TimeSpan timeSpan = timeExample.Subtract(timeExample);
            myStruct mystr = new myStruct(client, null, 0, 0, t, 0, 0, 0, false, timeSpan, timeSpan,null);
            MemoryStream stream = new MemoryStream();
            BinaryFormatter bformat = new BinaryFormatter();
            bformat.Serialize(stream, mystr);
            byte[] data = stream.ToArray();
          //  byte[] buff = new byte[BUFERSIZE];
         //   buff = stream.ToArray();//convert to bytes
           // client.Send(buff);
            //txtResult.AppendText(" Init end. "+mystr.Count);
            ClientState state = new ClientState();
            state.Client = client;

            //add prefix to data
            state.DataToSend = new byte[data.Length + 4];
            byte[] prefix = BitConverter.GetBytes(data.Length);
            //copy data size prefix
            Buffer.BlockCopy(prefix, 0, state.DataToSend, 0, prefix.Length);
            //copy the data
            Buffer.BlockCopy(data, 0, state.DataToSend, prefix.Length, data.Length);

            client.BeginSend(state.DataToSend, 0, state.DataToSend.Length,
              SocketFlags.None, new AsyncCallback(ClientSendCallback), state);
        }

        void TinhTich(object obj)
        {
            DateTime timeStartCalculate = DateTime.Now;
            Params p = (Params)obj;
            int h, c, k;
            for (h = p.sr; h < p.er; h++)
                for (c = 0; c < N; c++)
                {
                    double s = 0;
                    for (k = 0; k < N; k++)
                        s = s + A[h, k] * B[k, c];
                    C[h, c] = s;
                }
            stateLst[p.id] = 1;
            DateTime timeEndCalculate = DateTime.Now;
            System.TimeSpan timediff = timeEndCalculate.Subtract(timeStartCalculate);
            timeListCalculateClient.Add(timediff);
            if (txtResult.Text.Length > 0)
            {
                txtResult.AppendText(Environment.NewLine);
            }
            txtResult.AppendText(String.Format(" thoi gian tinh toan {0:d2} phut {1:d2} giay {2:d3} ms", timediff.Minutes, timediff.Seconds, timediff.Milliseconds));
        }
        private void ClientSendCallback(IAsyncResult ar)
        {
           
            ClientState state = (ClientState)ar.AsyncState;
            SocketError socketError;
            int sentData = state.Client.EndSend(ar, out socketError);
            if (socketError != SocketError.Success)
            {
                state.Client.Close();
                return;
            }

            state.DataSent += sentData;

            if (state.DataSent != state.DataToSend.Length)
            {   //not all data was sent
                state.Client.BeginSend(state.DataToSend, state.DataSent,
                  state.DataToSend.Length - state.DataSent, SocketFlags.None,
                    new AsyncCallback(ClientSendCallback), state);
            }
            else
            {   //all data was sent
                if (txtResult.Text.Length > 0)
                {
                    txtResult.AppendText(Environment.NewLine);
                }
                txtResult.AppendText(String.Format(" all data was sent{0}", state.DataToSend.Length));
                if (finish == 1)
                {
                    DateTime t2 = DateTime.Now;
                    diffTimeSend = t2.Subtract(t1);
                    myStruct mystr = new myStruct(client, null, 2, 0, 0, 0, 0, 0, false, diffTimeSend, difffff, timeListCalculateClient);
                    MemoryStream stream = new MemoryStream();
                    BinaryFormatter bformat = new BinaryFormatter();
                    bformat.Serialize(stream, mystr);
                    byte[] data = stream.ToArray();
                    //  byte[] buff = new byte[BUFERSIZE];
                    //   buff = stream.ToArray();//convert to bytes
                    // client.Send(buff);
                    //txtResult.AppendText(" Init end. "+mystr.Count);
                    ClientState state1 = new ClientState();
                    state1.Client = client;

                    //add prefix to data
                    state1.DataToSend = new byte[data.Length + 4];
                    byte[] prefix = BitConverter.GetBytes(data.Length);
                    //copy data size prefix
                    Buffer.BlockCopy(prefix, 0, state1.DataToSend, 0, prefix.Length);
                    //copy the data
                    Buffer.BlockCopy(data, 0, state1.DataToSend, prefix.Length, data.Length);
                    finish = finish + 1;
                    client.BeginSend(state1.DataToSend, 0, state1.DataToSend.Length,
                      SocketFlags.None, new AsyncCallback(ClientSendCallback), state1);
                }
                else
                {
                    finish = finish + 1;
                }
               
               // state.Client.Close();
            }
        }

        private void ServerReadCallback(IAsyncResult ar)
        {
            ServerState state = (ServerState)ar.AsyncState;
            Socket client = state.Client;
            SocketError socketError;

            int dataRead = client.EndReceive(ar, out socketError);
            int dataOffset = 0; //to simplify logic

            if (socketError != SocketError.Success)
            {
               client.Close();
               return;
            }

            if (dataRead <= 0)             
            { //connection reset 
               client.Close();                 
               return;             
            } 
         
            if (!state.DataSizeReceived)             
            {
               if (dataRead >= 4)
               {   //we received data size prefix
                   state.DataSize = BitConverter.ToInt32(state.Buffer, 0);
                   state.DataSizeReceived = true;
                   dataRead -= 4;
                   dataOffset += 4;
               }
            }

            if ((state.Data.Length + dataRead) == state.DataSize)
            {   //we have all the data
                state.Data.Write(state.Buffer, dataOffset, dataRead);
                myStruct data = MaHoa(state.Data.ToArray());
                DateTime timereceive1= DateTime.Now;
                difffff= timereceive1.Subtract(timereceive);
                if (txtResult.Text.Length > 0)
                {
                    txtResult.AppendText(Environment.NewLine);
                }
                txtResult.AppendText(String.Format(" thoi gian receive la {0:d2} phut {1:d2} giay {2:d3} ms", difffff.Minutes, difffff.Seconds, difffff.Milliseconds));
                //Console.WriteLine("Data received. Size: {0}", state.DataSize);
                if (txtResult.Text.Length > 0)
                {
                    txtResult.AppendText(Environment.NewLine);
                }
                txtResult.AppendText("we have all the data ");
                datareceiving = false;
               
               Calculate(data);
               // client.Close();
                return;
             }
             else
             {   //there is still data pending, store what we've
                 //received and issue another BeginReceive
                 state.Data.Write(state.Buffer, dataOffset, dataRead);

                 client.BeginReceive(state.Buffer, 0, state.Buffer.Length,
                   SocketFlags.None, new AsyncCallback(ServerReadCallback), state);
             }
        }
        private void clientsite_Load(object sender, EventArgs e)
        {

        }
    }
}